#include "album_pano.h"
#include "file_utils.h"
#include "elog.h"
#include "elog_file.h"
#include "album_common.h"
#include "img_utils.h"

#include <stdlib.h>

#include <string>
#include <filesystem>
#include <algorithm>

#include <opencv2/opencv.hpp>

namespace fs = std::filesystem;

static sqlite3* m_db = nullptr;
static std::string panorama_table_name = "panoramaInfo";
static std::string panorama_folder_path = album_path + panorama_path;


AlbumPano::AlbumPano() {
}

AlbumPano::~AlbumPano() {

}

// 自定义比较函数，按照文件名中的行列进行排序
bool AlbumPano::compareFilenames(const std::string& a, const std::string& b) {
    // 提取文件名
    std::string file_name_a = a.substr(a.find_last_of('/') + 1);
    std::string file_name_b = b.substr(b.find_last_of('/') + 1);

    // 提取a和b的行列数
    int row_a, col_a, row_b, col_b;
    try {
        row_a = std::stoi(file_name_a.substr(0, 2));
        col_a = std::stoi(file_name_a.substr(3, 2));
        row_b = std::stoi(file_name_b.substr(0, 2));
        col_b = std::stoi(file_name_b.substr(3, 2));
    } catch (const std::invalid_argument& ex) {
        log_e("Error: Unable to parse row and column numbers from the filename");
        return false;
    }

    // 按照行列数进行排序
    if (row_a < row_b)
        return true;
    else if (row_a > row_b)
        return false;
    else
        return col_a < col_b;
}

/*
    全景缩略图拼接
*/
concatenate_result AlbumPano::concatenateImages(const std::string& input_folder, const std::string& output_image_path) {
    concatenate_result result;
    result.code = 0;
    // Get a list of all image files in the input folder
    std::vector<std::string> image_files;
    for (const auto& entry : fs::directory_iterator(input_folder)) {
        if (entry.is_regular_file()) {
            std::string file_name = entry.path().filename().string();
            if (file_name.length() == 10 && file_name.substr(2, 1) == "_" && file_name.substr(5, 1) == ".") {
                image_files.push_back(entry.path().string());
            }
        }
    }

    if (image_files.size() < 9) {
        result.code = -1;
        return result;
    }

    // Sort the image files in ascending order based on the row and column numbers
    std::sort(image_files.begin(), image_files.end(), compareFilenames);


    log_i("Sorted image_files: ");
    for (const auto& file : image_files) {
        log_i("%s", file.c_str());
    }
    
    // Get the number of rows and columns from the first image file
    std::string last_image_name = image_files.back().substr(image_files.back().find_last_of('/') + 1);
    int num_rows, num_cols;
    try {
        num_rows = std::stoi(last_image_name.substr(0, 2)) + 1; // Adding 1 to account for 0-based indexing
        num_cols = std::stoi(last_image_name.substr(3, 2)) + 1; // Adding 1 to account for 0-based indexing
    } catch (const std::invalid_argument& ex) {
        log_e("Error: Unable to parse row and column numbers from the filename.");
        result.code = -1;
        return result;
    }

    // Check if the number of image files matches the expected number of rows and columns
    int expected_num_images = num_rows * num_cols;
    if (image_files.size() != expected_num_images) {
        log_e("Error: The number of images in the folder does not match the expected number of rows and columns.");
        result.code = -1;
        return result;
    }

    // Create a canvas for the concatenated image
    cv::Mat canvas(num_rows * cv::imread(image_files[0]).rows, num_cols * cv::imread(image_files[0]).cols, CV_8UC3, cv::Scalar(255, 255, 255));

    // Concatenate the images to the canvas
    int idx = 0;
    for (int r = 0; r < num_rows; ++r) {
        for (int c = 0; c < num_cols; ++c) {
            cv::Mat small_img = cv::imread(image_files[idx++]);
            cv::Rect roi(c * small_img.cols, r * small_img.rows, small_img.cols, small_img.rows);
            small_img.copyTo(canvas(roi));
        }
    }

    // Save the concatenated image
    cv::imwrite(output_image_path, canvas);
    fileSync();

    result.row = num_rows;
    result.col = num_cols;
    
    return result;
}

int AlbumPano::createPanoInfoTable(sqlite3* db) {
    char* errMsg = 0;
    int rc;

    m_db = db;

    // 创建panoramaInfo表（如果还未创建）
    const char* createTableSQL = R"(
        CREATE TABLE IF NOT EXISTS panoramaInfo (
            id INTEGER PRIMARY KEY,
            file_path TEXT UNIQUE,
            modification_time INTEGER,
            thumbnail_path TEXT,
            file_size INTEGER,
            row INTEGER,
            col INTEGER,
            width TEXT,
            height TEXT,
            media_type INTEGER
        );
    )";

    rc = sqlite3_exec(db, createTableSQL, NULL, 0, &errMsg);
    if (rc != SQLITE_OK) {
        log_e("cannot create table: %s", errMsg);
        sqlite3_free(errMsg);
    } else {
        log_i("create table successfully");
    }

    return 0;
    
}

int AlbumPano::insertPanoInfo(const PanoInfo& panorama_info) {
    log_i("insertPanoInfo file_path = %s", panorama_info.common_info.file_path.c_str());
    if (!m_db) {
        log_e("The database connection is not initialized");
        return -1;
    }

    std::string insertDataSQL = "INSERT INTO " + panorama_table_name + " (file_path, modification_time, thumbnail_path, file_size, row, col, width, height, media_type) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);";

    sqlite3_stmt* stmt;
    int rc = sqlite3_prepare_v2(m_db, insertDataSQL.c_str(), -1, &stmt, nullptr);
    if (rc != SQLITE_OK) {
        log_e("can not prepare sql: %s", sqlite3_errmsg(m_db));
        return -1;
    }

    sqlite3_bind_text(stmt, 1, panorama_info.common_info.file_path.c_str(), -1, SQLITE_STATIC);
    sqlite3_bind_int64(stmt, 2, panorama_info.common_info.modification_time);
    sqlite3_bind_text(stmt, 3, panorama_info.common_info.thumbnail_path.c_str(), -1, SQLITE_STATIC);
    sqlite3_bind_int64(stmt, 4, panorama_info.common_info.file_size);
    sqlite3_bind_int(stmt, 5, panorama_info.unique_info.row);
    sqlite3_bind_int(stmt, 6, panorama_info.unique_info.col);
    sqlite3_bind_int(stmt, 7, panorama_info.common_info.width);
    sqlite3_bind_int(stmt, 8, panorama_info.common_info.height);
    sqlite3_bind_int(stmt, 9, panorama_info.common_info.media_type);

    rc = sqlite3_step(stmt);
    if (rc != SQLITE_DONE) {
        log_e("can not insert :%s", sqlite3_errmsg(m_db));
        sqlite3_finalize(stmt);
        return -1;
    }

    sqlite3_finalize(stmt);

    return 0;
}

int AlbumPano::getCount() {
    int count = 0;

    if (!m_db) {
        log_e("The database connection is not initialized");
        return count;
    }

    std::string sql = "SELECT COUNT(*) FROM " + panorama_table_name + ";";
    sqlite3_stmt* stmt;

    int rc = sqlite3_prepare_v2(m_db, sql.c_str(), -1, &stmt, nullptr);
    if (rc != SQLITE_OK) {
        log_e("can not prepare sql: %s", sqlite3_errmsg(m_db));
        return count;
    }

    if (sqlite3_step(stmt) == SQLITE_ROW) {
        count = sqlite3_column_int(stmt, 0);
    }

    sqlite3_finalize(stmt);

    return count;
}

std::vector<PanoInfo> AlbumPano::queryPanoInfoByTime() {
    std::vector<PanoInfo> result;

    if (!m_db) {
        log_e("The database connection is not initialized");
        return result;
    }

    std::string sql = "SELECT * FROM " + panorama_table_name + " ORDER BY modification_time DESC;";
    sqlite3_stmt* stmt;

    int rc = sqlite3_prepare_v2(m_db, sql.c_str(), -1, &stmt, nullptr);
    if (rc != SQLITE_OK) {
        log_e("can not prepare sql: %s", sqlite3_errmsg(m_db));
        return result;
    }

    while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
        PanoInfo info;
        info.common_info.file_path = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1));
        info.common_info.modification_time = sqlite3_column_int64(stmt, 2);
        info.common_info.thumbnail_path = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 3));
        info.common_info.file_size = sqlite3_column_int64(stmt, 4);
        info.unique_info.row = sqlite3_column_int(stmt, 5);
        info.unique_info.col = sqlite3_column_int(stmt, 6);
        info.common_info.width = sqlite3_column_int(stmt, 7);
        info.common_info.height = sqlite3_column_int(stmt, 8);
        info.common_info.media_type = sqlite3_column_int(stmt, 9);
        result.push_back(info);
    }

    sqlite3_finalize(stmt);

    return result;
}

std::vector<PanoInfo> AlbumPano::queryPanoInfoByTime(int page, int items_per_page) {
    std::vector<PanoInfo> result;

    if (!m_db) {
        log_e("The database connection is not initialized");
        return result;
    }

    std::string sql = "SELECT * FROM " + panorama_table_name + " ORDER BY modification_time DESC LIMIT ? OFFSET ?;";
    sqlite3_stmt* stmt;

    int rc = sqlite3_prepare_v2(m_db, sql.c_str(), -1, &stmt, nullptr);
    if (rc != SQLITE_OK) {
        log_e("can not prepare sql: %s", sqlite3_errmsg(m_db));
        return result;
    }

    int offset = (page - 1) * items_per_page;
    sqlite3_bind_int(stmt, 1, items_per_page);
    sqlite3_bind_int(stmt, 2, offset);

    while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
        PanoInfo info;
        info.common_info.file_path = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1));
        info.common_info.modification_time = sqlite3_column_int64(stmt, 2);
        info.common_info.thumbnail_path = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 3));
        info.common_info.file_size = sqlite3_column_int64(stmt, 4);
        info.unique_info.row = sqlite3_column_int(stmt, 5);
        info.unique_info.col = sqlite3_column_int(stmt, 6);
        info.common_info.width = sqlite3_column_int(stmt, 7);
        info.common_info.height = sqlite3_column_int(stmt, 8);
        info.common_info.media_type = sqlite3_column_int(stmt, 9);
        result.push_back(info);
    }

    sqlite3_finalize(stmt);

    return result;
}


    
std::vector<PanoInfo> AlbumPano::queryAllPanoInfo() {
    std::vector<PanoInfo> result;

    if (!m_db) {
        log_e("The database connection is not initialized");
        return result;
    }

    std::string sql = "SELECT * FROM " + panorama_table_name + ";";
    sqlite3_stmt* stmt;

    int rc = sqlite3_prepare_v2(m_db, sql.c_str(), -1, &stmt, nullptr);
    if (rc != SQLITE_OK) {
        log_e("can not prepare sql: %s", sqlite3_errmsg(m_db));
        return result;
    }

    while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
        PanoInfo info;
        info.common_info.file_path = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1));
        info.common_info.modification_time = sqlite3_column_int64(stmt, 2);
        info.common_info.thumbnail_path = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 3));
        info.common_info.file_size = sqlite3_column_int64(stmt, 4);
        info.unique_info.row = sqlite3_column_int(stmt, 5);
        info.unique_info.col = sqlite3_column_int(stmt, 6);
        info.common_info.width = sqlite3_column_int(stmt, 7);
        info.common_info.height = sqlite3_column_int(stmt, 8);
        info.common_info.media_type = sqlite3_column_int(stmt, 9);
        result.push_back(info);
    }

    sqlite3_finalize(stmt);

    return result;
}

std::vector<PanoInfo> AlbumPano::queryPanoInfoByFilepath(const std::string& file_path) {
    std::vector<PanoInfo> result;

    if (!m_db) {
        log_e("The database connection is not initialized");
        return result;
    }

    std::string sql = "SELECT * FROM " + panorama_table_name + " WHERE file_path = ?";
    sqlite3_stmt* stmt;

    int rc = sqlite3_prepare_v2(m_db, sql.c_str(), -1, &stmt, nullptr);
    if (rc != SQLITE_OK) {
        log_e("can not prepare sql: %s", sqlite3_errmsg(m_db));
        return result;
    }

    sqlite3_bind_text(stmt, 1, file_path.c_str(), -1, SQLITE_STATIC);

    while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
        PanoInfo info;
        info.common_info.file_path = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1));
        info.common_info.modification_time = sqlite3_column_int64(stmt, 2);
        info.common_info.thumbnail_path = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 3));
        info.common_info.file_size = sqlite3_column_int64(stmt, 4);
        info.unique_info.row = sqlite3_column_int(stmt, 5);
        info.unique_info.col = sqlite3_column_int(stmt, 6);
        info.common_info.width = sqlite3_column_int(stmt, 7);
        info.common_info.height = sqlite3_column_int(stmt, 8);
        info.common_info.media_type = sqlite3_column_int(stmt, 9);
        result.push_back(info);
    }

    sqlite3_finalize(stmt);

    return result;
}

bool AlbumPano::updatePanoInfo(const std::string& file_path, const std::string& new_filename) {
    if (!m_db) {
        log_e("The database connection is not initialized");
        return false;
    }

    std::string sql = "UPDATE " + panorama_table_name + " SET file_path = ? WHERE file_path = ?;";
    sqlite3_stmt* stmt;

    int rc = sqlite3_prepare_v2(m_db, sql.c_str(), -1, &stmt, nullptr);
    if (rc != SQLITE_OK) {
        log_e("can not prepare sql: %s", sqlite3_errmsg(m_db));
        return false;
    }

    sqlite3_bind_text(stmt, 1, new_filename.c_str(), -1, SQLITE_STATIC);
    sqlite3_bind_text(stmt, 2, file_path.c_str(), -1, SQLITE_STATIC);

    rc = sqlite3_step(stmt);
    if (rc != SQLITE_DONE) {
        log_e("can not update: %s", sqlite3_errmsg(m_db));
        sqlite3_finalize(stmt);
        return false;
    }

    sqlite3_finalize(stmt);

    return true;
}
    
bool AlbumPano::deletePanoInfo(const std::string& file_path) {
    log_i("deletePanoInfo file_path = %s", file_path.c_str());

    if (!m_db) {
        log_e("The database connection is not initialized");
        return false;
    }

    std::string sql = "DELETE FROM " + panorama_table_name + " WHERE file_path = ?;";
    sqlite3_stmt* stmt;

    int rc = sqlite3_prepare_v2(m_db, sql.c_str(), -1, &stmt, nullptr);
    if (rc != SQLITE_OK) {
        log_e("can not prepare sql: %s", sqlite3_errmsg(m_db));
        return false;
    }

    sqlite3_bind_text(stmt, 1, file_path.c_str(), -1, SQLITE_STATIC);

    rc = sqlite3_step(stmt);
    if (rc != SQLITE_DONE) {
        log_e("can not delete: %s", sqlite3_errmsg(m_db));
        sqlite3_finalize(stmt);
        return false;
    }

    sqlite3_finalize(stmt);
    
    return true;
}
    
void AlbumPano::printPanoInfo(const PanoInfo& panorama_info) {
    log_i("panorama_info file_path = %s", panorama_info.common_info.file_path.c_str());
    log_i("panorama_info modification_time = %ld", panorama_info.common_info.modification_time);
    log_i("panorama_info thumbnail_path = %s", panorama_info.common_info.thumbnail_path.c_str());
    log_i("panorama_info file_size = %d", panorama_info.common_info.file_size);
    log_i("panorama_info row = %d, col = %d", panorama_info.unique_info.row, panorama_info.unique_info.col);
}

bool AlbumPano::deleteDirectory(const std::string& file_path) {
    std::string pano_dir = extractFolderPath(file_path, 1);

    if (pano_dir != "" && isPathContains(pano_dir, panorama_folder_path) && pano_dir.size() > panorama_folder_path.size()) {
        if (deletePanoInfo(file_path)) {
            deleteFolder(pano_dir);
            return true;
        } else {
            return false;
        }
    }

    return false;
}
    
bool AlbumPano::renameDirectory(const std::string& file_path, const std::string& new_astro_name) {
    std::string pano_dir = extractFolderPath(file_path, 1);

    if (pano_dir != "" && isPathContains(pano_dir, panorama_folder_path) && pano_dir.size() > panorama_folder_path.size()) {
        std::string new_pano_path = panorama_folder_path + new_astro_name;
        if (renameFile(pano_dir, new_pano_path)) {
            updatePanoInfo(pano_dir, new_pano_path);
            return true;
        } else {
            return false;
        }
    } 
    
    return false;
}

/// @brief 全景拍摄完成后，更新全景数据库
/// @param folder_path 全景图片文件夹路径
/// @return
int AlbumPano::updatePano(const std::string& folder_path, int rows, int cols) {
    log_i("updatePano folder_path = %s", folder_path.c_str());

    if (folder_path != "" && isPathContains(folder_path, panorama_folder_path) && folder_path.size() > panorama_folder_path.size()) {
        PanoInfo info;
        info.common_info.modification_time = getFileModifiedTime(folder_path);
        info.unique_info.row = rows;
        info.unique_info.col = cols;
        info.common_info.media_type = MEDIA_PANO;
        info.common_info.file_size = getTotalSizeOfFilesWithExtension(folder_path, ".jpg");

        std::string first_file_path = folder_path + "/" + "00_00.jpg";
        std::string dest_thumbnail_path;
        if (fs::exists(first_file_path)) {
            dest_thumbnail_path = folder_path +  + "/panorama_thumbnail.jpg";
            if (getImageSize(dest_thumbnail_path).width < album_thumbnail_width) {
                imageDownsample(first_file_path, dest_thumbnail_path, album_thumbnail_width, album_thumbnail_height);
            }
            std::vector<PanoInfo> find_pano_info = queryPanoInfoByFilepath(first_file_path);
            if (!find_pano_info.empty())
                return -1;
            info.common_info.file_path = first_file_path;
            // imageDownsample(first_file_path, dest_thumbnail_path, album_thumbnail_width, album_thumbnail_height);

            if (fs::exists(dest_thumbnail_path)) {
                info.common_info.thumbnail_path = dest_thumbnail_path;
                insertPanoInfo(info);
                fileSync();
                return 0;
            }
        }
        
        return 0;
    }

    return -1;
}